﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Interop;
using System.Windows;
using System.Windows.Media;

namespace WrWpf.Vista
{
    #region MARGINS
    [StructLayout(LayoutKind.Sequential)]
    internal struct MARGINS
    {
        public MARGINS(Thickness t)
        {
            Left = (int)t.Left;
            Right = (int)t.Right;
            Top = (int)t.Top;
            Bottom = (int)t.Bottom;
        }

        public int Left;
        public int Right;
        public int Top;
        public int Bottom;
    }

    #endregion


    public static class AeroEffect
    {
        #region Constants
        internal static readonly bool IsVista = System.Environment.OSVersion.Version.Major >= 6;
        #endregion

        #region APIs
        /// <summary>
        /// DwmExtendFrameIntoClientArea provides a way to transform a normal form to Aero Glass form.
        /// </summary>
        /// <param name="hWnd">a window handle</param>
        /// <param name="pMarInset">a MARGINS struct that contains information on how much extra the DWM should extend the frame on the top, left, right and bottom sides of the screen.</param>
        /// <returns></returns>
        /// <remarks>
        /// The one big challenge with glass is getting alpha-blending to work correctly. 
        /// Without using alpha-blending, then the content that you place on the glass will overwrite 
        /// the glass itself and make it invisible. This is a problem in GDI, since it has no awareness 
        /// of an alpha channel, but it's a little easier with GDI+. In your Windows Forms application, 
        /// you simply need to set the TransparencyKey property to a color that you won't use elsewhere 
        /// in the application (I use Gainsboro, for reasons that will become apparent later). 
        /// Then you can create one or more panels that are docked to the margins of your form and set 
        /// the background color for the panel to the transparency key. Now when you call 
        /// DwmExtendFrameIntoClientArea, the glass will show within its margins wherever you've set 
        /// something of the appropriate transparency key.
        /// </remarks>
        /// <example>
        /// MARGINS margins = new MARGINS();
        /// margins.cxLeftWidth = 0;
        /// margins.cxRightWidth = 0;
        /// margins.cyTopHeight = 45;
        /// margins.cyBottomHeight = 0;
        /// 
        /// IntPtr hWnd = this.Handle; 
        /// int result = DwmExtendFrameIntoClientArea(hWnd, ref margins);
        /// </example>
        [DllImport("dwmapi.dll", PreserveSig = false)]
        internal static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS pMarInset);

        [DllImport("dwmapi.dll", PreserveSig = false)]
        internal static extern bool DwmIsCompositionEnabled();
        #endregion

        #region Functions
        /// <summary>
        /// Extends the glass frame into the client area of any WPF Window based on the passed-in margin.
        /// </summary>
        /// <param name="window">The window.</param>
        /// <param name="margin">The margin.</param>
        /// <returns></returns>
        /// <example>
        /// protected override void OnSourceInitialized(EventArgs e)
        /// {
        ///     base.OnSourceInitialized(e);
        ///     // This can't be done any earlier than the SourceInitialized event:
        ///     GlassHelper.ExtendGlassFrame(this, new Thickness(-1));
        /// }
        /// </example>
        public static bool ExtendGlassFrame(Window window, Thickness margin)
        {
            if (AeroEffect.IsVista)
            {
                try
                {
                    if (DwmIsCompositionEnabled())
                    {
                        IntPtr hwnd = new WindowInteropHelper(window).Handle;
                        if (hwnd != IntPtr.Zero)
                        {
                            // Set the background to transparent from both the WPF and Win32 perspectives
                            window.Background = System.Windows.Media.Brushes.Transparent;
                            HwndSource.FromHwnd(hwnd).CompositionTarget.BackgroundColor =
                                System.Windows.Media.Colors.Transparent;
                            MARGINS margins = new MARGINS(margin);
                            DwmExtendFrameIntoClientArea(hwnd, ref margins);
                            return true;
                        }
                        else
                            throw new InvalidOperationException("The Window must be shown before extending glass.");
                    }
                    else
                    {
                        return false;
                    }
                }
                catch (DllNotFoundException)
                {
                    //Just return false. We are running on non-Vista supported OS.
                    return false;
                }
            }
            else return false;
        }
        #endregion

        #region Properties
        //It is important to notice here that all attached properties MUST be registered with the 
        //DependencyProperty.RegisterAttached and that they MUST also have a Get<PropertyName> and 
        //Set<PropertyName>!!!
        public static readonly DependencyProperty IsEnabledProperty =
            DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(AeroEffect),
            new PropertyMetadata(OnIsEnabledChanged));

        public static void SetIsEnabled(DependencyObject element, bool value)
        {
            element.SetValue(IsEnabledProperty, value);
        }

        public static bool GetIsEnabled(DependencyObject element)
        {
            return (bool)element.GetValue(IsEnabledProperty);
        }

        private static void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if ((obj is Window) && (bool)args.NewValue)
            {
                Window window = (Window)obj;
                window.Loaded += new RoutedEventHandler(window_Loaded);   
            }
        }

        static void window_Loaded(object sender, RoutedEventArgs e)
        {
            ExtendGlassFrame((Window)sender, new Thickness(-1));
        }
        #endregion
    }
}
